package com.tn.rtdata;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.tn.openrpc.RemoteMethod;

/**
 * <code>DataSource</code> is the base class that subscribes for and receives fill updates.
 */
public abstract class DataSource<T>
{
  private static final Logger LOGGER = LoggerFactory.getLogger(DataSource.class);

  private RemoteMethod dataEventStatsUpdateMethod;
  private List<DataListener<T>> dataListeners;

  /**
   * Creates a new <code>DataSource</code>.
   *
   * @param dataEventStatsUpdateMethod the <code>RemoteMethod</code> that called to update the data-event stats.
   */
  public DataSource(RemoteMethod dataEventStatsUpdateMethod)
  {
    this.dataEventStatsUpdateMethod = dataEventStatsUpdateMethod;
    this.dataListeners = new ArrayList<DataListener<T>>();
  }

  /**
   * Returns the running flag.
   */
  public abstract boolean isRunning();

  /**
   * Adds the <code>dataListener</code>.
   */
  public void addDataListener(DataListener<T> dataListener)
  {
    dataListeners.add(dataListener);
  }

  /**
   * Adds the <code>dataListener</code>.
   */
  public void removeDataListener(DataListener<T> dataListener)
  {
    dataListeners.remove(dataListener);
  }

  /**
   * Starts this <code>DataSource</code>.
   *
   * @throws FillException if an error occurs starting.
   */
  public abstract void start() throws FillException;

  /**
   * Stops this <code>DataSource</code>.
   *
   * @throws FillException if an error occurs stopping.
   */
  public abstract void stop() throws FillException;

  /**
   * Fires the <code>DataListener.dataEvent(DataEvent)</code>.
   */
  protected void fireDataEvent(DataEvent<T> event)
  {
    updateDataEventStats(event);

    for (DataListener<T> dataListener : dataListeners)
    {
      dataListener.dataEvent(event);
    }
  }

  /**
   * Updates the the data event stats.
   */
  protected void updateDataEventStats(DataEvent<T> event)
  {
    try
    {
      dataEventStatsUpdateMethod.invoke(event.getId());
    }
    catch (Throwable e)
    {
      LOGGER.error("An error occurred updating the data event stats.", e);
    }
  }

  /**
   * Process the <code>messageContent</code> firing <code>DataEvent</code>s as appropriate.
   */
  protected void processMessageContent(Object messageContent)
  {
    if (messageContent instanceof DataEvent)
    {
      //noinspection unchecked
      fireDataEvent((DataEvent<T>)messageContent);
    }
    else
    {
      LOGGER.warn("Unrecognized message content received: " + messageContent);
    }
  }
}
